#pragma once
#include <SimpleDef.h>
#include <Utility/intrinsic.h>
#include <IntExcept/Debug.h>


typedef struct __CPUInfo
{
    WORD wIntFlag; /* 中断标志位 */
    SHORT sCLICount; /* 关中断次数 */
} CPUInfo;

extern CPUInfo g_CPUInfo[512];

typedef struct __SpinLock
{
    DWORD dwCPUId; /* 持有锁的 CPU */
    BOOL bLockStatus; /* 锁，第 0 位有效，其余位忽略，TRUE 表示持有 */
} SpinLock, *LPSpinLock;

#define INVALID_CPU_ID (0xFFFFFFFF)

static inline BOOL CurrentCoreHasLock(LPSpinLock lpSpinLock)
{
    DWORD dwEFLAGS_IF = READ_EFLAGS_32() & 0x200; /* 保存中断标志位 */
    __asm cli /* 关中断 */
    BOOL bHasLock = (lpSpinLock->bLockStatus & 0x1) == 0 && /* 0 表示上锁 */
        GetCurrentCPUCore() == lpSpinLock->dwCPUId; /* 当前 CPU 持有锁 */
    WRITE_EFLAGS_32(READ_EFLAGS_32() & dwEFLAGS_IF); /* 恢复中断标志位 */
    return bHasLock;
}
static inline void PushIntFlag()
{    
    __asm cli
    if (g_CPUInfo[GetCurrentCPUCore()].sCLICount == 0) /* 第一次关中断 */
        g_CPUInfo[GetCurrentCPUCore()].wIntFlag = READ_EFLAGS_32() & 0x200;
    g_CPUInfo[GetCurrentCPUCore()].sCLICount++;
}

static inline void PopIntFlag()
{
    KERNEL_ASSERT((READ_EFLAGS_32() & 0x200) == 0, ""); /* 一定处于关中断状态 */
    g_CPUInfo[GetCurrentCPUCore()].sCLICount--;
    KERNEL_ASSERT(g_CPUInfo[GetCurrentCPUCore()].sCLICount >= 0, ""); /* 关中断次数不可能为负数 */
    if (g_CPUInfo[GetCurrentCPUCore()].sCLICount == 0) /* 关中断次数减少为 0，恢复初始状态 */
        WRITE_EFLAGS_32(READ_EFLAGS_32() & g_CPUInfo[GetCurrentCPUCore()].wIntFlag);
}

static inline void AquireSpinLock(LPSpinLock lpSpinLock)
{
    PushIntFlag();
    __asm cli /* 获取锁前先关中断 */
    /* 已经上锁且上锁者为自己的情况永远不应发生 */
    KERNEL_ASSERT(!CurrentCoreHasLock(lpSpinLock), ""); /* 当前进程不应该申请已经持有的锁 */
    __asm /* 尝试获取锁，test and reset */ 
    {
        mov eax, lpSpinLock
    TestAndReset:
        lock btr dword ptr [eax].SpinLock.bLockStatus, 0
        jnc TestAndReset /* 一直检测，直到拿到 1，表示锁成功获取，此时锁的值为 0 */
    }
    lpSpinLock->dwCPUId = GetCurrentCPUCore(); /* 当前 CPU 占有锁 */
}

static inline void ReleaseSpinLock(LPSpinLock lpSpinLock)
{
    KERNEL_ASSERT((lpSpinLock->bLockStatus & 0x1) == 0 && lpSpinLock->dwCPUId == GetCurrentCPUCore(), ""); /* 一定是当前 CPU 核心持有锁的情况下尝试释放锁 */
    lpSpinLock->dwCPUId = INVALID_CPU_ID;
    lpSpinLock->bLockStatus = 0; /* 此条语句一旦执行，自旋的其他 CPU 立即获得锁 */
    PopIntFlag();
}
